#include "spi_dev_ctl.h"
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
#include "usr_cfg.h"
#include "zlog.h"


spi_cfg_info afe1_spi_dev;

static int read_spi_config(spi_cfg_info *config)
{
    uint8_t u8;
    uint32_t u32;

    if (ioctl(config->fd, SPI_IOC_RD_MODE, &u8) < 0)
    {
        perror("read_spi_config() SPI_IOC_RD_MODE error!\n");
		dzlog_error("read_spi_config() SPI_IOC_RD_MODE error!");
        return FAILURE;
    }
    config->spi_mode = u8 & 0x03;
    config->spi_ready = ((config->spi_mode & SPI_READY) ? 1 : 0);

    if (ioctl(config->fd, SPI_IOC_RD_LSB_FIRST, &u8) < 0)
    {
        perror("read_spi_config() SPI_IOC_RD_LSB_FIRST error!\n");
		dzlog_error("read_spi_config() SPI_IOC_RD_LSB_FIRST error!");
        return FAILURE;
    }
    config->lsb_first = (u8 == SPI_LSB_FIRST ? 1 : 0);

    if (ioctl(config->fd, SPI_IOC_RD_BITS_PER_WORD, &u8) < 0)
    {
        perror("read_spi_config() SPI_IOC_RD_BITS_PER_WORD error!\n");
		dzlog_error("read_spi_config() SPI_IOC_RD_BITS_PER_WORD error!");
        return FAILURE;
    }
    config->bits_per_word = (u8 == 0 ? 8 : u8);

    if (ioctl(config->fd, SPI_IOC_RD_MAX_SPEED_HZ, &u32) < 0)
    {
        perror("read_spi_config() SPI_IOC_RD_MAX_SPEED_HZ error!\n");
		dzlog_error("read_spi_config() SPI_IOC_RD_MAX_SPEED_HZ error!");
        return FAILURE;
    }
    config->spi_speed = u32;

    return SUCCESS;
}

static int write_spi_config(spi_cfg_info *config)
{
    uint8_t u8;
    uint32_t u32;

    u8 = config->spi_mode;
    if (config->spi_ready == 1)
        u8 |= SPI_READY;
    if (ioctl(config->fd, SPI_IOC_WR_MODE, &u8) < 0)
    {
    	perror("write_spi_config() SPI_IOC_WR_MODE error!\n");
		dzlog_error("write_spi_config() SPI_IOC_WR_MODE error!");
        return FAILURE;
    }

    u8 = (config->lsb_first ? SPI_LSB_FIRST : 0);
    if (ioctl(config->fd, SPI_IOC_WR_LSB_FIRST, &u8) < 0)
    {
        perror("write_spi_config() SPI_IOC_WR_LSB_FIRST error!\n");
		dzlog_error("write_spi_config() SPI_IOC_WR_LSB_FIRST error!");
        return FAILURE;
    }

    u8 = config->bits_per_word;
    if (ioctl(config->fd, SPI_IOC_WR_BITS_PER_WORD, &u8) < 0)
    {
    	perror("write_spi_config() SPI_IOC_WR_BITS_PER_WORD error!\n");
		dzlog_error("write_spi_config() SPI_IOC_WR_BITS_PER_WORD error!");
        return FAILURE;
    }

    u32 = config->spi_speed;
    if (ioctl(config->fd, SPI_IOC_WR_MAX_SPEED_HZ, &u32) < 0)
    {
    	perror("write_spi_config() SPI_IOC_WR_MAX_SPEED_HZ error!\n");
		dzlog_error("write_spi_config() SPI_IOC_WR_MAX_SPEED_HZ error!");
        return FAILURE;
    }

    return SUCCESS;
}

static int spi_open(sys_cfg_info *sys, spi_cfg_info *spi_dev, const char *dev_path, spi_cfg_info *cfg_new)
{
    spi_cfg_info pre_spi_cfg;

    pre_spi_cfg.fd = open(dev_path, O_RDONLY);
    if (pre_spi_cfg.fd < 0)
    {
        perror("pre_spi_cfg.fd = open() error!\n");
		dzlog_error("pre_spi_cfg.fd = open() error!");
        exit(FAILURE);
    }

    if (read_spi_config(&pre_spi_cfg) != 0)
    {
        perror("read_spi_config() error!\n");
		dzlog_error("read_spi_config() error!");
		close(pre_spi_cfg.fd);
        exit(FAILURE);
    }

    if (sys->debug == ON)
    {
        printf("pre spi mode:0x%x\n", pre_spi_cfg.spi_mode);
        printf("pre spi ready:0x%x\n", pre_spi_cfg.spi_ready);
        printf("pre spi lsb_first:0x%x\n", pre_spi_cfg.lsb_first);
        printf("pre spi bits_per_word:0x%x\n", pre_spi_cfg.bits_per_word);
        printf("pre spi speed:%d\n", pre_spi_cfg.spi_speed);
    }

    if (cfg_new == NULL) // use pre config
    {
    	spi_dev->fd				= pre_spi_cfg.fd;
        spi_dev->spi_mode 		= pre_spi_cfg.spi_mode;
        spi_dev->spi_speed 		= pre_spi_cfg.spi_speed;
        spi_dev->lsb_first 		= pre_spi_cfg.lsb_first;
        spi_dev->spi_ready 		= pre_spi_cfg.spi_ready;
        spi_dev->bits_per_word 	= pre_spi_cfg.bits_per_word;
    } else
    { 	// use new config
        //! todo need Check parameter
        spi_dev->spi_mode 		= cfg_new->spi_mode;
        spi_dev->lsb_first 		= cfg_new->lsb_first;
        spi_dev->bits_per_word 	= cfg_new->bits_per_word;
        spi_dev->spi_ready 		= cfg_new->spi_ready;
        spi_dev->spi_speed 		= cfg_new->spi_speed;

        return write_spi_config(spi_dev);
    }

    return SUCCESS;
}

int spi_transfer(spi_cfg_info *spi_dev, const char tx[], char rx[], uint32_t len)
{
    struct spi_ioc_transfer transfer;
    /*    struct spi_ioc_transfer transfer = {
            .tx_buf = 0,
            .rx_buf = 0,
            .len = 0,
            .delay_usecs = 0,
            .speed_hz = 0,
            .bits_per_word = 0,
        };*/
    memset(&transfer, 0, sizeof(transfer));

    transfer.rx_buf = (unsigned long)rx;
    transfer.tx_buf = (unsigned long)tx;
    transfer.len = len;
    transfer.delay_usecs = CMD_SPI_DELAY;

    if (ioctl(spi_dev->fd, SPI_IOC_MESSAGE(1), &transfer) < 0)
        return FAILURE;

    return SUCCESS;
}


int afe1_spi_init(sys_cfg_info *sys, spi_cfg_info *spi_dev)
{
    spi_cfg_info *cfg_new = NULL; //! HK36B828A spi1 Can't config, set NULL.
    
    if (spi_open(sys, spi_dev, SPI1_DEV_PATH, cfg_new) < 0 )
		return FAILURE;
	else
		return SUCCESS;
}


